/**
 * Copyright (C), 2001-2023, www.bosssof.com.cn
 * FileName: RenewTokenServiceImpl
 * Author: Administrator
 * Date: 2023-04-26 15:30:42
 * Description:
 * <p>
 * History:
 * <author> Administrator
 * <time> 2023-04-26 15:30:42
 * <version> 1.0.0
 * <desc> 版本描述
 */
package com.sd365.gateway.authorization.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.sd365.common.core.common.exception.BusinessException;
import com.sd365.common.core.common.exception.code.BizErrorCode;
import com.sd365.common.util.JwtTokenBuilder;
import com.sd365.common.util.TokenUtil;
import com.sd365.gateway.authorization.constant.BusinessResultConsts;
import com.sd365.gateway.authorization.service.RenewTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

/**
 * @ClassName: RenewTokenServiceImpl
 * @Description: 类的主要功能描述
 * @Author: Administrator
 * @Date: 2023-04-26 15:30
 **/
@RefreshScope
@Service
public class RenewTokenServiceImpl implements RenewTokenService {
    /**
     * Jwt Token签名秘钥
     */
    @Value("${app.secret.token.key}")
    private String secret="$6$bosssoft$";
    /**
     * 默认设置10天的token有效期
     */
    @Value("${app.secret.token.period}")
    private Long period = 864000L;
    /**
     * 默认设置一天的提前提醒时间 举例如果有效期10天则在第9天提醒
     */
    @Value("${app.secret.token.remind}")
    private Long remind=86400L;
    /**
     * 续期时间三天 单位毫秒 1000 * 60 * 60 * 24 * 3 = 86400
     */
    private static final Long p = 259200000L;

    private static final String TOKEN_USER_ID_KEY = "remote:cache:token:user:";

    @Autowired
    private JwtTokenBuilder jwtTokenBuilder;
    /**
     * 操作redis token
     */
    @Resource(name = "tokenRedisTemplate")
    private RedisTemplate<String,Object> redisTemplate;
    @Override
    public String renewToken(String userId) {
        Assert.hasText(userId,"userId为空, renewToken()参数异常");
        // 从redis中获取token 是整个jwt token的 JSONString 格式
        Object tokenString = redisTemplate.opsForValue().get(TOKEN_USER_ID_KEY + userId);
        if(ObjectUtils.isEmpty(tokenString)){
           throw new BusinessException(BizErrorCode.DATA_SEARCH_NOT_FOUND,
                  String.format("无法通过key=%s 从缓存找到token",userId));
        }
        // 更新 更新 token的 过期时间
             List<String> jwtTokenItemList= TokenUtil.parseToken(String.valueOf(tokenString));
         updateTokenPayloadExpiresAt(JSONObject.parseObject(jwtTokenItemList.get(1)));

        // 重新签名token
         String signatureToken=   jwtTokenBuilder.build().buildHead(JSONObject.parseObject(jwtTokenItemList.get(0)))
                 .buildPayload(JSONObject.parseObject(jwtTokenItemList.get(1)))
                 .buildSignature(secret).toString();

        // 重新更新 redis token
            redisTemplate.opsForValue().set(TOKEN_USER_ID_KEY+userId,signatureToken);
        // 返回新的token
        return signatureToken;

    }

    @Override
    public int expire(Long userId,String token) {
        Object jwtToken=redisTemplate.opsForValue().get(TOKEN_USER_ID_KEY+userId);
        if(ObjectUtils.isEmpty(jwtToken)){
            // 返回过期 前端根据这个需要重新登录
            return BusinessResultConsts.TOKEN_EXPIRE_TURE;
        }else{
            // 解析token 比较过期时间 和 当前时间
            JSONObject joPayload=TokenUtil.parseTokenPayload(token);
            // 取决认证服务放进去的是什么类型
            Long expiresTime=Long.parseLong(String.valueOf(joPayload.get("expiresAt")));
            // remind 为 配置文件设置的提醒到期的时间 *1000为微妙毫秒
           if  ((expiresTime-System.currentTimeMillis()) <=remind*1000){
               return BusinessResultConsts.TOKEN_EXPIRE_NEARLY;
           }else{
               // 没有到过期时间 还处在有效期
               return BusinessResultConsts.TOKEN_EXPIRE_FALSE;
           }
        }
    }

    /**
     * 更新过期时间用于重新计算token签名
     * @param payload  JWT 的 payload部分
     * @return payload
     */
   private JSONObject updateTokenPayloadExpiresAt(JSONObject payload) {
        Assert.notNull(payload,"payload updateTokenExpiresAt参数错误");
        payload.put("expiresAt",new Date(System.currentTimeMillis() + period*1000));
        return payload;
    }


}
